{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "08fe26e7",
   "metadata": {},
   "source": [
    "iris数据集，Iris数据集是常用的分类实验数据集，由Fisher, 1936收集整理。Iris也称鸢尾花卉数据集，是一类多重变量分析的数据集。数据集包含150个数据样本，分为3类，每类50个数据，每个数据包含4个属性。可通过花萼长度，花萼宽度，花瓣长度，花瓣宽度4个属性预测鸢尾花卉属于（Setosa，Versicolour，Virginica）三个种类中的哪一类。\n",
    "\n",
    "接下来，我们通过实际的数据集，来学习深度学习的一般流程。\n",
    "\n",
    "下载数据集：\n",
    "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data\n",
    "\n",
    "* 将数据集拆分成训练集和测试集\n",
    "shuf iris.data | split -l $(( 151* 80 / 100 )) \n",
    "* 该命令会产出xaa,xbb两个文件，重命名这两个文件\n",
    "* mv xaa train.csv && mv xbb test.csv\n",
    "\n",
    "\n",
    "一些说明：\n",
    "1. 为什么要拆分训练集和测试集？\n",
    "2. 数据特征的归一化处理\n",
    "3. 数据加载、随机批量训练方式\n",
    "5. 训练收敛\n",
    "6. 欠拟合，过拟合问题：\n",
    "6. 使用网格搜索调整DNN的超参数：学习率，网络深度，隐藏层大小等；"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1a9b5dc9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "36 30\n",
      "(array([7.6, 3. , 6.6, 2.1], dtype=float32), 2)\n",
      "Feature batch shape: torch.Size([2, 4])\n",
      "Labels batch shape: torch.Size([2])\n",
      "features0: tensor([5.4000, 3.9000, 1.7000, 0.4000])\n",
      "features1: tensor([5.4000, 3.9000, 1.7000, 0.4000])\n",
      "label: tensor(0)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import torch\n",
    "import torch.nn.functional as F\n",
    "import torch.nn as nn\n",
    "from torch.utils.data import Dataset\n",
    "from torch.utils.data import DataLoader\n",
    "\n",
    "torch.manual_seed(0)\n",
    "\n",
    "\n",
    "#1，定义数据集\n",
    "class IrisDataset(Dataset):\n",
    "    def __init__(self,data_type=\"train\"):\n",
    "        assert data_type in ('train','test')\n",
    "        self.labels = {'Iris-setosa':0,'Iris-versicolor':1,'Iris-virginica':2}\n",
    "        self.pd_frame = pd.read_csv(\"./dataset/iris/%s.csv\" % (data_type),header=None )\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.pd_frame)\n",
    "\n",
    "    def __getitem__(self, idx):\n",
    "        label = self.pd_frame.iloc[idx, 4]\n",
    "        X = self.pd_frame.iloc[idx, 0:4]\n",
    "        return X.to_numpy(np.float32),self.labels[label]\n",
    "\n",
    "# 单个test\n",
    "train = IrisDataset(\"train\")\n",
    "test = IrisDataset(\"test\")\n",
    "print(len(train),len(test))\n",
    "print(train[0])\n",
    "\n",
    "# 批量加载测试\n",
    "train_dataloader = DataLoader(train, batch_size=2, shuffle=True)\n",
    "train_features, train_labels = next(iter(train_dataloader))\n",
    "print(f\"Feature batch shape: {train_features.size()}\")\n",
    "print(f\"Labels batch shape: {train_labels.size()}\")\n",
    "\n",
    "features = train_features[0]\n",
    "print(\"features0:\",features)\n",
    "features = features.squeeze()\n",
    "print(\"features1:\",features)\n",
    "label = train_labels[0]\n",
    "print(\"label:\",label)\n",
    "\n",
    "\n",
    "test_dataloader = DataLoader(test, batch_size=2, shuffle=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5ae38a16",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 4])\n",
      "tensor([[5.5000, 3.5000, 1.3000, 0.2000],\n",
      "        [5.2000, 4.1000, 1.5000, 0.1000]])\n",
      "多个测试: tensor([[-0.2501, -0.7116, -0.0021],\n",
      "        [-0.2437, -0.7377,  0.0563]], grad_fn=<AddmmBackward>)\n",
      "torch.Size([2, 3]) torch.Size([2])\n"
     ]
    }
   ],
   "source": [
    "# 2. 定义模型\n",
    "class IrisModel(nn.Module):\n",
    "    def __init__(self, input_dim, output_dim):\n",
    "        super(IrisModel, self).__init__()\n",
    "        self.fc_0 = nn.Linear(input_dim, 25) \n",
    "        self.fc_1 = nn.Linear(25, 30) \n",
    "        self.fc_2 = nn.Linear(30, output_dim) \n",
    "\n",
    "    def forward(self, x):\n",
    "        out = F.relu(self.fc_0(x))\n",
    "        out = F.relu(self.fc_1(out))\n",
    "        out = self.fc_2(out)\n",
    "#         print(\"out:\",out.shape,out)\n",
    "#         out = F.softmax(out, dim=1)\n",
    "        return out\n",
    "\n",
    "# 模型初始化\n",
    "model = IrisModel(4,3) #特征的维度=4，iris有3个分类\n",
    "\n",
    "# 单条测试\n",
    "# x = torch.tensor(train[0][0]).view(1,-1)\n",
    "# print(x,x.shape)\n",
    "# output = model(x)\n",
    "# print(\"单条测试:\", output)\n",
    "\n",
    "# 多个测试\n",
    "train_features, train_labels = next(iter(train_dataloader))\n",
    "print(train_features.shape)\n",
    "print(train_features)\n",
    "pred = model(train_features)\n",
    "print(\"多个测试:\",pred)\n",
    "print(pred.shape,train_labels.shape)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a202862b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(1.0770, grad_fn=<NllLossBackward>)\n"
     ]
    }
   ],
   "source": [
    "# 3. 定义损失函数，优化算法等\n",
    "learning_rate = 0.001\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) #定义最优化算法\n",
    "\n",
    "criterion = nn.CrossEntropyLoss()  \n",
    "\n",
    "loss = criterion(pred,train_labels)\n",
    "print(loss)\n",
    "\n",
    "#error: cross_entropy 1D target tensor expected, multi-target not supported"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a3782a13",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss: 1.675363  [    0/   36]\n",
      "loss: 0.899168  [   10/   36]\n",
      "loss: 0.829396  [   20/   36]\n",
      "loss: 1.261095  [   30/   36]\n",
      "loss: 1.045605  [   30/   36]\n",
      "Epoch 1\n",
      "-------------------------------\n",
      "loss: 0.928218  [    0/   36]\n",
      "loss: 1.050659  [   10/   36]\n",
      "loss: 0.899680  [   20/   36]\n",
      "loss: 1.273160  [   30/   36]\n",
      "loss: 1.197524  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 30.0%, Avg loss: 1.183782 \n",
      "\n",
      "Epoch 2\n",
      "-------------------------------\n",
      "loss: 1.279933  [    0/   36]\n",
      "loss: 0.786806  [   10/   36]\n",
      "loss: 1.274202  [   20/   36]\n",
      "loss: 0.895924  [   30/   36]\n",
      "loss: 1.314132  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 50.0%, Avg loss: 1.157805 \n",
      "\n",
      "Epoch 3\n",
      "-------------------------------\n",
      "loss: 0.961736  [    0/   36]\n",
      "loss: 0.825188  [   10/   36]\n",
      "loss: 1.168127  [   20/   36]\n",
      "loss: 1.205422  [   30/   36]\n",
      "loss: 1.253262  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.133702 \n",
      "\n",
      "Epoch 4\n",
      "-------------------------------\n",
      "loss: 1.275472  [    0/   36]\n",
      "loss: 0.915248  [   10/   36]\n",
      "loss: 1.147025  [   20/   36]\n",
      "loss: 0.845344  [   30/   36]\n",
      "loss: 1.206982  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.112071 \n",
      "\n",
      "Epoch 5\n",
      "-------------------------------\n",
      "loss: 0.814360  [    0/   36]\n",
      "loss: 1.209013  [   10/   36]\n",
      "loss: 1.189471  [   20/   36]\n",
      "loss: 0.771802  [   30/   36]\n",
      "loss: 1.209507  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.092627 \n",
      "\n",
      "Epoch 6\n",
      "-------------------------------\n",
      "loss: 0.814208  [    0/   36]\n",
      "loss: 1.193684  [   10/   36]\n",
      "loss: 0.769558  [   20/   36]\n",
      "loss: 0.802495  [   30/   36]\n",
      "loss: 1.150225  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.075196 \n",
      "\n",
      "Epoch 7\n",
      "-------------------------------\n",
      "loss: 0.745665  [    0/   36]\n",
      "loss: 1.130999  [   10/   36]\n",
      "loss: 0.810866  [   20/   36]\n",
      "loss: 1.505281  [   30/   36]\n",
      "loss: 0.738515  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.058738 \n",
      "\n",
      "Epoch 8\n",
      "-------------------------------\n",
      "loss: 0.806804  [    0/   36]\n",
      "loss: 0.741432  [   10/   36]\n",
      "loss: 0.730810  [   20/   36]\n",
      "loss: 1.057255  [   30/   36]\n",
      "loss: 0.809236  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.042597 \n",
      "\n",
      "Epoch 9\n",
      "-------------------------------\n",
      "loss: 0.743086  [    0/   36]\n",
      "loss: 0.780736  [   10/   36]\n",
      "loss: 0.755842  [   20/   36]\n",
      "loss: 1.461553  [   30/   36]\n",
      "loss: 0.751560  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.026848 \n",
      "\n",
      "Epoch 10\n",
      "-------------------------------\n",
      "loss: 0.714715  [    0/   36]\n",
      "loss: 0.661942  [   10/   36]\n",
      "loss: 0.708803  [   20/   36]\n",
      "loss: 0.733081  [   30/   36]\n",
      "loss: 1.133432  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 1.011478 \n",
      "\n",
      "Epoch 11\n",
      "-------------------------------\n",
      "loss: 0.719600  [    0/   36]\n",
      "loss: 1.102116  [   10/   36]\n",
      "loss: 1.453964  [   20/   36]\n",
      "loss: 0.656408  [   30/   36]\n",
      "loss: 1.034365  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.997049 \n",
      "\n",
      "Epoch 12\n",
      "-------------------------------\n",
      "loss: 0.678832  [    0/   36]\n",
      "loss: 1.093132  [   10/   36]\n",
      "loss: 0.665123  [   20/   36]\n",
      "loss: 0.683075  [   30/   36]\n",
      "loss: 0.686613  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.983302 \n",
      "\n",
      "Epoch 13\n",
      "-------------------------------\n",
      "loss: 0.679933  [    0/   36]\n",
      "loss: 0.666325  [   10/   36]\n",
      "loss: 1.037162  [   20/   36]\n",
      "loss: 1.072862  [   30/   36]\n",
      "loss: 1.078560  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.969514 \n",
      "\n",
      "Epoch 14\n",
      "-------------------------------\n",
      "loss: 1.014711  [    0/   36]\n",
      "loss: 0.646181  [   10/   36]\n",
      "loss: 0.700698  [   20/   36]\n",
      "loss: 1.044410  [   30/   36]\n",
      "loss: 1.027861  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.956454 \n",
      "\n",
      "Epoch 15\n",
      "-------------------------------\n",
      "loss: 1.029278  [    0/   36]\n",
      "loss: 0.674363  [   10/   36]\n",
      "loss: 0.674908  [   20/   36]\n",
      "loss: 1.036834  [   30/   36]\n",
      "loss: 1.033918  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.943281 \n",
      "\n",
      "Epoch 16\n",
      "-------------------------------\n",
      "loss: 0.638283  [    0/   36]\n",
      "loss: 0.649045  [   10/   36]\n",
      "loss: 1.023777  [   20/   36]\n",
      "loss: 0.655108  [   30/   36]\n",
      "loss: 1.380204  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.930772 \n",
      "\n",
      "Epoch 17\n",
      "-------------------------------\n",
      "loss: 0.589470  [    0/   36]\n",
      "loss: 0.629943  [   10/   36]\n",
      "loss: 0.530409  [   20/   36]\n",
      "loss: 1.035979  [   30/   36]\n",
      "loss: 1.393040  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.918028 \n",
      "\n",
      "Epoch 18\n",
      "-------------------------------\n",
      "loss: 0.585225  [    0/   36]\n",
      "loss: 0.568627  [   10/   36]\n",
      "loss: 0.679778  [   20/   36]\n",
      "loss: 0.595402  [   30/   36]\n",
      "loss: 0.942553  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.906038 \n",
      "\n",
      "Epoch 19\n",
      "-------------------------------\n",
      "loss: 0.533084  [    0/   36]\n",
      "loss: 0.623800  [   10/   36]\n",
      "loss: 0.981166  [   20/   36]\n",
      "loss: 0.931721  [   30/   36]\n",
      "loss: 0.952683  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.893920 \n",
      "\n",
      "Epoch 20\n",
      "-------------------------------\n",
      "loss: 1.017698  [    0/   36]\n",
      "loss: 0.578377  [   10/   36]\n",
      "loss: 0.642871  [   20/   36]\n",
      "loss: 1.359778  [   30/   36]\n",
      "loss: 0.545778  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.882301 \n",
      "\n",
      "Epoch 21\n",
      "-------------------------------\n",
      "loss: 0.645446  [    0/   36]\n",
      "loss: 0.564602  [   10/   36]\n",
      "loss: 0.555709  [   20/   36]\n",
      "loss: 0.873950  [   30/   36]\n",
      "loss: 1.311967  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.869987 \n",
      "\n",
      "Epoch 22\n",
      "-------------------------------\n",
      "loss: 0.962005  [    0/   36]\n",
      "loss: 0.906039  [   10/   36]\n",
      "loss: 0.595170  [   20/   36]\n",
      "loss: 0.918720  [   30/   36]\n",
      "loss: 0.576574  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.859107 \n",
      "\n",
      "Epoch 23\n",
      "-------------------------------\n",
      "loss: 0.430316  [    0/   36]\n",
      "loss: 0.940649  [   10/   36]\n",
      "loss: 0.556753  [   20/   36]\n",
      "loss: 0.864499  [   30/   36]\n",
      "loss: 0.499261  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.848065 \n",
      "\n",
      "Epoch 24\n",
      "-------------------------------\n",
      "loss: 0.559000  [    0/   36]\n",
      "loss: 0.920031  [   10/   36]\n",
      "loss: 0.984552  [   20/   36]\n",
      "loss: 0.449063  [   30/   36]\n",
      "loss: 0.960721  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.837001 \n",
      "\n",
      "Epoch 25\n",
      "-------------------------------\n",
      "loss: 0.518054  [    0/   36]\n",
      "loss: 0.404491  [   10/   36]\n",
      "loss: 0.926467  [   20/   36]\n",
      "loss: 0.875013  [   30/   36]\n",
      "loss: 0.907072  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.826664 \n",
      "\n",
      "Epoch 26\n",
      "-------------------------------\n",
      "loss: 0.824624  [    0/   36]\n",
      "loss: 0.939061  [   10/   36]\n",
      "loss: 0.837216  [   20/   36]\n",
      "loss: 0.532950  [   30/   36]\n",
      "loss: 0.441691  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.816539 \n",
      "\n",
      "Epoch 27\n",
      "-------------------------------\n",
      "loss: 0.911625  [    0/   36]\n",
      "loss: 0.887409  [   10/   36]\n",
      "loss: 1.302160  [   20/   36]\n",
      "loss: 0.507971  [   30/   36]\n",
      "loss: 0.886034  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.806589 \n",
      "\n",
      "Epoch 28\n",
      "-------------------------------\n",
      "loss: 1.288706  [    0/   36]\n",
      "loss: 0.435721  [   10/   36]\n",
      "loss: 0.560967  [   20/   36]\n",
      "loss: 0.372520  [   30/   36]\n",
      "loss: 0.775439  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.796914 \n",
      "\n",
      "Epoch 29\n",
      "-------------------------------\n",
      "loss: 0.481801  [    0/   36]\n",
      "loss: 1.292598  [   10/   36]\n",
      "loss: 0.489556  [   20/   36]\n",
      "loss: 0.784311  [   30/   36]\n",
      "loss: 0.925527  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.787159 \n",
      "\n",
      "Epoch 30\n",
      "-------------------------------\n",
      "loss: 0.458787  [    0/   36]\n",
      "loss: 0.475517  [   10/   36]\n",
      "loss: 0.930358  [   20/   36]\n",
      "loss: 0.379274  [   30/   36]\n",
      "loss: 0.844994  [   30/   36]\n",
      "Test Error: \n",
      " Accuracy: 60.0%, Avg loss: 0.777652 \n",
      "\n",
      "Done!\n"
     ]
    }
   ],
   "source": [
    "# 4 启动训练\n",
    "def train(dataloader, model, loss_fn, optimizer):\n",
    "    size = len(dataloader.dataset)\n",
    "    model.train() #训练模式\n",
    "    for batch, (X, y) in enumerate(dataloader):         \n",
    "        pred = model(X)\n",
    "        loss = criterion(pred,y)\n",
    "\n",
    "        # Backpropagation\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        if batch % 5 == 0:\n",
    "            loss, current = loss.item(), batch * len(X)\n",
    "            print(f\"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]\")\n",
    "            \n",
    "    print(f\"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]\")\n",
    "\n",
    "train(train_dataloader, model, criterion, optimizer)\n",
    "\n",
    "def test(dataloader, model, loss_fn):\n",
    "    size = len(dataloader.dataset)\n",
    "    num_batches = len(dataloader)\n",
    "    model.eval() #预测模式\n",
    "    test_loss, correct = 0, 0\n",
    "    with torch.no_grad():\n",
    "        for X, y in dataloader:\n",
    "#             X, y = X.to(device), y.to(device)\n",
    "            pred = model(X)\n",
    "            test_loss += loss_fn(pred, y).item()\n",
    "            correct += (pred.argmax(1) == y).type(torch.float).sum().item()\n",
    "    test_loss /= num_batches\n",
    "    correct /= size\n",
    "    print(f\"Test Error: \\n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \\n\")\n",
    "    \n",
    "epochs = 30\n",
    "for t in range(epochs):\n",
    "    print(f\"Epoch {t+1}\\n-------------------------------\")\n",
    "    train(train_dataloader, model, criterion, optimizer)\n",
    "    test(test_dataloader, model, criterion)\n",
    "print(\"Done!\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
